package com.zhou.org.controller;


import com.zhou.framework.constant.CacheKeyEnum;
import com.zhou.framework.redis.util.RedisUtil;
import com.zhou.framework.util.PageUtil;
import com.zhou.framework.util.SessionUtil;
import com.zhou.framework.util.SettingUtil;
import com.zhou.util.*;
import com.zhou.framework.aop.anotation.LogOption;
import com.zhou.framework.aop.enums.LogLevel;
import com.zhou.framework.aop.enums.PassWordStrongerEnum;
import com.zhou.framework.config.PasswordPolicyConfig;
import com.zhou.framework.dto.IntegersDto;
import com.zhou.framework.exception.LoginException;
import com.zhou.framework.exception.VerifyException;
import com.zhou.framework.jwt.model.LoginUser;
import com.zhou.framework.jwt.model.OnlineUser;
import com.zhou.framework.model.PageResult;
import com.zhou.framework.model.RS;
import com.zhou.framework.util.EnumClassUtil;
import com.zhou.org.common.PasswordUtil;
import com.zhou.org.dao.User;
import com.zhou.org.dao.UserPasswordRecord;
import com.zhou.org.dto.*;
import com.zhou.org.model.OrgTree;
import com.zhou.org.service.OrgService;
import com.zhou.org.service.UserPasswordRecordService;
import com.zhou.org.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author lang.zhou
 * @since 2022-08-23
 */
@Api(tags = "用户管理接口")
@RestController
@Slf4j
@RequestMapping("/sys/user")
public class UserController {
    @Autowired
    private UserService userService;
    @Autowired
    private OrgService orgService;
    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private UserPasswordRecordService passwordRecordService;

    @ApiOperation("id查询用户")
    @LogOption(value = LogLevel.QUERY,response = true)
    @GetMapping("/get/{userId}")
    public RS getUserDto(@PathVariable Integer userId){
        User user = userService.getUserDto(userId);
        return RS.data(user);
    }

    @ApiOperation("查询当前登录用户")
    @LogOption(value = LogLevel.QUERY,response = true)
    @GetMapping("/current")
    public RS current(){
        Integer userId = SessionUtil.getCurrentUserId();
        User user = userService.getUserDto(userId);
        UserInfoDto userDto = new UserInfoDto(user);
        List<OrgTree> parent = orgService.getParentOrgObject(user.getOrgId());
        userDto.setParent(parent);
        if(NumberUtil.upper0(user.getSuperior())){
            User superior = userService.getUserDto(user.getSuperior());
            if(superior != null){
                userDto.setSuperiorName(superior.getRealName());
            }
        }
        return RS.data(userDto);
    }

    @ApiOperation("修改用户")
    @LogOption(value = LogLevel.EDIT,response = true)
    @RequestMapping(value = "/save", method = {RequestMethod.POST,RequestMethod.GET})
    public RS save(@Validated UserUpdateDto dto){
        User user = userService.saveUser(dto);
        return RS.data(user.getUserId());
    }

    @ApiOperation("用户修改密码")
    @LogOption(value = LogLevel.PASSWORD,response = true)
    @PostMapping("/changePassword")
    public Object changePassword(@RequestBody @Validated ChangePasswordDto dto){
        PasswordPolicyConfig config = PasswordPolicyConfig.getInstance();
        try{
            User user = userService.getById(SessionUtil.getCurrentUserId());
            String priKey = SettingUtil.getString("system", "pri_key","");

            String oldPwd = PasswordUtil.decrypt(dto.getOldPassword(), priKey);
            String newPwd = PasswordUtil.decrypt(dto.getNewPassword(), priKey);
            Assert.isTrue(!Objects.equals(oldPwd,newPwd),"两次输入的密码不能一样");

            String hex = Md5Util.hex(oldPwd, user.getSalt());
            Assert.equals(hex,user.getPassword(),"原密码不正确");

            String newHex = Md5Util.hex(newPwd, user.getSalt());
            if(config.isEnable()){
                boolean b1 = config.getMinLength() <= dto.getNewPassword().length();
                Assert.isTrue(b1, "密码长度最少为"+config.getMinLength()+"位");

                int stronger = PasswordUtil.getPasswordStronger(newPwd);
                String name = EnumClassUtil.getName(PassWordStrongerEnum.class, config.getPasswordStronger());
                Assert.isTrue(stronger >= config.getPasswordStronger(),"密码强度至少为" + name);

                int c = config.getPasswordRepeatCount();
                List<UserPasswordRecord> recordList = passwordRecordService.listRecordCount(user.getUserId(), c);
                for (int i = 0, recordsSize = recordList.size(); i < recordsSize; i++) {
                    if(i <= c-1){
                        UserPasswordRecord record = recordList.get(i);
                        String pv2 = record.getNewPassword();
                        Assert.isTrue(!Objects.equals(newHex,pv2),"新密码不得与最近" + c + "次相同");
                    }
                }
            }

            userService.changeUserPassword(user.getUserId(),hex,newHex);
        }catch (LoginException | VerifyException e){
            return RS.failed(e.getMessage());
        } catch (Exception e){
            log.error("修改密码失败：",e);
            return RS.failed("修改密码失败");
        }
        return RS.success();
    }

    @ApiOperation("用户列表分页查询")
    @LogOption(LogLevel.QUERY)
    @PostMapping("/list")
    public Object list(@RequestBody UserQueryDto dto){
        PageResult<User> r = new PageResult<>(dto);
        PageUtil.offsetPage(r.getPageInfo());
        List<User> list = userService.getPageList(dto);
        for (User user : list) {
            List<OrgTree> parentTree = orgService.findLatestParentCache(user.getOrgId());
            user.setParent(parentTree);
        }
        return PageUtil.result(dto,list);
    }
    @ApiOperation("在线用户列表分页查询")
    @LogOption(LogLevel.QUERY)
    @PostMapping("/onlineList")
    public Object onlineList(@RequestBody OnlineUserQueryDto dto){
        Collection<String> keys = redisUtil.keys(CacheKeyEnum.LOGIN_USER_KEY + "*");
        List<OnlineUser> res = new ArrayList<>(keys.size());
        for (String fullKey : keys) {
            String key = fullKey.replace(CacheKeyEnum.LOGIN_USER_KEY, "");
            LoginUser cacheUser = SessionUtil.getCacheUser(key);
            OnlineUser user = new OnlineUser();
            BeanUtils.copyProperties(cacheUser,user);
            user.setRealName(cacheUser.getUser().getRealName());
            user.setUserName(cacheUser.getUser().getUsername());
            user.setActiveTime(TimeUtil.formatLongDate(cacheUser.getActiveTime(),"yyyy-MM-dd HH:mm:ss"));
            List<OrgTree> parentTree = orgService.findLatestParentCache(cacheUser.getUser().getOrgId());
            user.setParent(parentTree);

            boolean b1 = StringTool.isBlank(dto.getIp()) || user.getIp().contains(dto.getIp());
            boolean b2 = StringTool.isBlank(dto.getUserName()) || user.getUserName().contains(dto.getUserName())
                    || user.getRealName().contains(dto.getUserName());
            if(b1 && b2){
                res.add(user);
            }
        }

        return PageUtil.logicResult(dto,res);
    }
    @ApiOperation("注销在线用户")
    @LogOption(LogLevel.DELETE)
    @DeleteMapping("/logoutUser")
    public Object logoutUser(@RequestParam String key){
        SessionUtil.logout(key);
        return RS.success();
    }

    @DeleteMapping("/delete")
    @LogOption(value = LogLevel.DELETE,response = true)
    @ApiOperation("删除用户")
    public Object deleteUser(@RequestBody @Validated IntegersDto dto){
        userService.deleteUser(dto.getIds());
        return RS.success();
    }

    @ApiOperation("重置用户密码")
    @LogOption(value = LogLevel.EDIT,response = true)
    @PostMapping("/resetPassword")
    public Object resetPassword(@RequestBody @Validated IntegersDto dto){
        String df = SettingUtil.getDefaultPassword();
        userService.resetPassword(dto.getIds());
        return RS.data(df);
    }

    @ApiOperation("锁定用户")
    @LogOption(value = LogLevel.EDIT,response = true)
    @PostMapping("/lock")
    public Object lock(@RequestBody @Validated IntegersDto dto){
        userService.lock(dto.getIds());
        return RS.success();
    }

    @ApiOperation("解锁用户")
    @LogOption(value = LogLevel.EDIT,response = true)
    @PostMapping("/unlock")
    public Object unlock(@RequestBody @Validated IntegersDto dto){
        userService.unlock(dto.getIds());
        return RS.success();
    }

    @SneakyThrows
    @ApiOperation("用户上传头像")
    @LogOption(value = LogLevel.FILE,response = true)
    @PostMapping("/uploadAvatar")
    public Object uploadAvatar(@RequestParam("avatar") MultipartFile file){
        String avatar = null;
        if(!file.isEmpty()){
            LoginUser loginUser = SessionUtil.getCurrentUser();
            avatar = userService.saveUserHead(file.getBytes(),file.getOriginalFilename(), loginUser.getUserId());
            loginUser.getUser().setRelativeAvatar(avatar);
            SessionUtil.cacheUser(loginUser);
        }
        return RS.data(avatar);
    }

    @ApiOperation("用户修改个人信息")
    @LogOption(value = LogLevel.EDIT,response = true)
    @PostMapping("/changeInfo")
    public Object changeInfo(@RequestBody UserChangeDto dto){
        LoginUser loginUser = SessionUtil.getCurrentUser();
        Integer userId = loginUser.getUserId();
        User user = userService.getById(userId);
        BeanUtils.copyProperties(dto,user);
        userService.updateById(user);
        loginUser.setUser(user);
        SessionUtil.cacheUser(loginUser);
        return RS.success();
    }
}

